package server

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

type Session struct {
	Uid     int
	Expires time.Time
}

var UidBusy [MaxSession]bool
var Sessions = make(map[string]Session) //sessionId => uid

func GetUidBySessionId(sessionId string) int {
	if sess, ok := Sessions[sessionId]; ok {
		return sess.Uid
	} else {
		return -1
	}
}

func CheckSessionExpire() {
	for i, v := range Sessions {
		if time.Now().After(v.Expires) {
			UidBusy[v.Uid] = false
			delete(Sessions, i) //Session已过期，删除之
		} else {
			UidBusy[v.Uid] = true
		}
	}
}

func GenerateSessionId(r *http.Request) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(r.RemoteAddr+time.Now().String())))
}

func GetFreeUid() int { //Free的意义应当是当前没有被租用，而不是当前是否在线
	CheckSessionExpire()
	for i, busy := range UidBusy {
		if !busy && i > 0 {
			return i
		}
	}
	return -1
}

// 使用户登录，创建新的uid和session记录并返回分配的uid
func login(w http.ResponseWriter, r *http.Request) int {
	uid := GetFreeUid()
	if uid == -1 {
		return -1
	}
	sessionId := GenerateSessionId(r)
	fmt.Printf("New uid = %d, corresponding SESSIONID = %s\n", uid, sessionId)
	Sessions[sessionId] = Session{Uid: uid, Expires: time.Now().Add(24 * time.Hour)}
	userCookie := &http.Cookie{
		Name:    "GOSESSIONID",
		Value:   sessionId,                            // 生成一个用户ID
		Path:    "/",                                  // 设置 Cookie 的作用路径
		Expires: time.Now().Add(356 * 24 * time.Hour), // 设置过期时间
	}
	http.SetCookie(w, userCookie) // 设置 Cookie
	return uid
}

// 返回用户的uid
func authentic(w http.ResponseWriter, r *http.Request) string {
	// 尝试从 Cookie 中读取用户标识
	userCookie, err := r.Cookie("GOSESSIONID")
	var uid int
	if err != nil { //如果用户cookie没有sessionId
		uid = login(w, r)
	} else {
		if sess, ok := Sessions[userCookie.Value]; ok {
			if time.Now().After(sess.Expires) { //若session已过期，则续租
				Sessions[userCookie.Value] = Session{Uid: sess.Uid, Expires: time.Now().Add(time.Hour * 24)}
			}
			uid = sess.Uid
		} else { //若服务器session中没有该sessionId的记录，可能是服务器重启过，则重新分配uid和session
			uid = login(w, r)
		}
	}
	return strconv.Itoa(uid)
}
